from decimal import Decimal, getcontext

from vector import Vector

getcontext().prec = 30


class Line(object):

    NO_NONZERO_ELTS_FOUND_MSG = 'No nonzero elements found'

    def __init__(self, normal_vector=None, constant_term=None):
        self.dimension = 2

        if not normal_vector:
            all_zeros = ['0']*self.dimension
            normal_vector = Vector(all_zeros)
        self.normal_vector = normal_vector

        if not constant_term:
            constant_term = Decimal('0')
        self.constant_term = Decimal(constant_term)

        self.set_basepoint()


    def set_basepoint(self):
        try:
            n = self.normal_vector
            c = self.constant_term
            basepoint_coords = ['0']*self.dimension

            initial_index = Line.first_nonzero_index(n)
            initial_coefficient = n[initial_index]

            basepoint_coords[initial_index] = c/initial_coefficient
            self.basepoint = Vector(basepoint_coords)

        except Exception as e:
            if str(e) == Line.NO_NONZERO_ELTS_FOUND_MSG:
                self.basepoint = None
            else:
                raise e


    def __str__(self):

        num_decimal_places = 3

        def write_coefficient(coefficient, is_initial_term=False):
            coefficient = round(coefficient, num_decimal_places)
            if coefficient % 1 == 0:
                coefficient = int(coefficient)

            output = ''

            if coefficient < 0:
                output += '-'
            if coefficient > 0 and not is_initial_term:
                output += '+'

            if not is_initial_term:
                output += ' '

            if abs(coefficient) != 1:
                output += '{}'.format(abs(coefficient))

            return output

        n = self.normal_vector

        try:
            initial_index = Line.first_nonzero_index(n)
            terms = [write_coefficient(n[i], is_initial_term=(i==initial_index)) + 'x_{}'.format(i+1)
                     for i in range(self.dimension) if round(n[i], num_decimal_places) != 0]
            output = ' '.join(terms)

        except Exception as e:
            if str(e) == self.NO_NONZERO_ELTS_FOUND_MSG:
                output = '0'
            else:
                raise e

        constant = round(self.constant_term, num_decimal_places)
        if constant % 1 == 0:
            constant = int(constant)
        output += ' = {}'.format(constant)

        return output

    def is_parallel(self,l_other):
        # 取两条直线的法向量，观察是否平行
        # 如果平行，在l_other上取2点与self做法向量，观察是否与两条直线的法向量都垂直
        vert_self = self.normal_vector
        vert_other = l_other.normal_vector

        return vert_self.is_parallel(vert_other)
    
    def is_one_line(self,l_other):
        a,b = self.normal_vector
        c,d = l_other.normal_vector
        k_1 = self.constant_term
        k_2 = l_other.constant_term

        p_1 = a/c
        p_2 = b/d
        p_3 = k_1/k_2

        return p_1 == p_2 and p_1 == p_3

    def get_point_by_x( self , x ):
        a,b = self.normal_vector
        d_x = Decimal(str(x))
        return ( self.constant_term - a * d_x ) / b

    def get_point_by_y( self , y ):
        a,b = self.normal_vector
        d_y = Decimal(str(y))
        return ( self.constant_term - b * d_y ) / a

    def interscetion( self , l_other ):
        a,b = self.normal_vector
        c,d = l_other.normal_vector
        k_1 = self.constant_term
        k_2 = l_other.constant_term

        p_x = ( d * k_1 - b * k_2 ) / ( a * d - b * c )
        p_y = (-1 * c * k_1 + a * k_2)/( a * d - b * c )

        return [p_x,p_y]

    @staticmethod
    def first_nonzero_index(iterable):
        for k, item in enumerate(iterable):
            if not MyDecimal(item).is_near_zero():
                return k
        raise Exception(Line.NO_NONZERO_ELTS_FOUND_MSG)


class MyDecimal(Decimal):
    def is_near_zero(self, eps=1e-10):
        return abs(self) < eps

l_1 = Line(Vector(['4.046','2.836']),1.21)
l_2 = Line(Vector(['10.115','7.09']),3.025)

print('is parallel:' + str(l_1.is_parallel(l_2)))
print('is one line:' + str(l_1.is_one_line(l_2)))

# l_3 = Line(Vector(['1','1']),0)
# l_4 = Line(Vector(['2','2']),0)

# print('is parallel:' + str(l_3.is_parallel(l_4)))
# print('is one line:' + str(l_3.is_one_line(l_4)))

# l_1 = Line(Vector(['7.204','3.182']),8.68)
# l_2 = Line(Vector(['8.172','4.114']),9.883)
# is_p = l_1.is_parallel(l_2)
# is_o = l_1.is_one_line(l_2)
# p_i = None
# if (is_p == False):
#     p_i = l_1.interscetion(l_2)


# print('is parallel:' + str(is_p))
# print('is one line:' + str(is_o))
# print('intersction:' + str(p_i))

l_1 = Line(Vector(['1.182','5.562']),6.744)
l_2 = Line(Vector(['1.773','8.343']),9.525)
is_p = l_1.is_parallel(l_2)
is_o = l_1.is_one_line(l_2)
p_i = None
if (is_p == False):
    p_i = l_1.interscetion(l_2)


# print('is parallel:' + str(is_p))
# print('is one line:' + str(is_o))
# print('intersction:' + str(p_i))